C++复习 explicit关键字

  1. 隐式的类类型转换
    1. 只允许一步类类型转换
    2. 抑制构造函数定义的隐式转换
  2. 类型转换运算符
  3. explicit 使用

隐式的类类型转换

  • explicit 修饰构造函数时,可以防止隐式转换和复制初始化
  • explicit 修饰类型转换函数时,可以防止隐式转换,但 按语境转换 除外
  • 转换构造函数

能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则

Sales_data(std::string s) : Sales_data(s,0,0){} // 可以隐式(stirng -> Sales_data)
Sales_data(std::istream &is) : Sales_data(){read(is,*this)} // 可以隐式(cin(istream) -> Sales_data)

只允许一步类类型转换

  • 对其他类型也只会允许一步类型转换
Sales_data::combine("99999"); //error char* -> string -> Sales_data
  • 可以显式隐式混用
Sales_data::combine(string("99999"));
Sales_data::combine(Sales_data("9999999"));

抑制构造函数定义的隐式转换

  • 关键字 explicit

  • 只能在类内的单个形参的构造函数前使用

  • 并且explicit声明的构造函数只能用于直接初始化而不能用于拷贝初始化

Sales_data item1(null_book); //true
Sales_data item2 = null_book; //error
  • explicit 使得构造函数不能用于隐式变换,但仍可以显式变换
item.combine(Sales_data(null_book));
item.combine(Static_cast<Sales_data>(cin));

类型转换运算符

  • 形式
operator type() const;
  • type表示某种类型
  • 可以面向除void以外的任意类型进行定义,并且要求能作为返回类型

    • 所以type不能是数组和函数,但可以是指向数组和函数的指针或绑定它们的引用
    • 没有返回类型,没有形参,必须是成员函数
  • 显式的类型转换运算符(c++11新特性)

    即显示的类型转换运算符(c++11新特性)(explicit operator)

    • 通常与explicit一样
    • 如果当表达式被用作条件,则编译其会将显式的类型转换自动应用于它。
      • if、while、for 的控制表达式;
      • 内建逻辑运算符 !、&& 和 || 的操作数;
      • 条件运算符 ?: 的首个操作数;
      • static_assert 声明中的谓词;
      • noexcept 说明符中的表达式;
      • explicit 说明符中的表达式。(C++20 起)
  • 无论什么时候在条件中使用流对象,都会为IO类型定义operator bool的隐式转换

explicit 使用

struct A
{
    A(int) { }
    operator bool() const { return true; }
};

struct B
{
    explicit B(int) {}
    explicit operator bool() const { return true; }
};

void doA(A a) {}

void doB(B b) {}

int main()
{
    A a1(1);        // OK:直接初始化
    A a2 = 1;        // OK:复制初始化
    A a3{ 1 };        // OK:直接列表初始化
    A a4 = { 1 };        // OK:复制列表初始化
    A a5 = (A)1;        // OK:允许 static_cast 的显式转换 
    doA(1);            // OK:允许从 int 到 A 的隐式转换
    if (a1);        // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
    bool a6(a1);        // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
    bool a7 = a1;        // OK:使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换
    bool a8 = static_cast<bool>(a1);  // OK :static_cast 进行直接初始化

    B b1(1);        // OK:直接初始化
    B b2 = 1;        // 错误:被 explicit 修饰构造函数的对象不可以复制初始化
    B b3{ 1 };        // OK:直接列表初始化
    B b4 = { 1 };        // 错误:被 explicit 修饰构造函数的对象不可以复制列表初始化
    B b5 = (B)1;        // OK:允许 static_cast 的显式转换
    doB(1);            // 错误:被 explicit 修饰构造函数的对象不可以从 int 到 B 的隐式转换
    if (b1);        // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换
    bool b6(b1);        // OK:被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换
    bool b7 = b1;        // 错误:被 explicit 修饰转换函数 B::operator bool() 的对象不可以隐式转换
    bool b8 = static_cast<bool>(b1);  // OK:static_cast 进行直接初始化

    return 0;
}